#include "collect.h"
// #include "msg.h"
#include <errno.h>
#include <string.h>

static date_update(uint16_t *buf,
            cfg_pu_t *pu,
            cfg_frame_t *mb,
            cfg_ch_t *ch)
{
    cfg_node_t *node = ch->node;

    switch (node->reg_type)
    {

    case CH_TYPE_FLOAT:
    {
        int pos = node->reg_add - mb->addr;

        switch (node->endian)
        {
        case CH_FORMAT_H1H2H3H4:
            ch->data = modbus_get_float_abcd(buf + pos);
            break;
        case CH_FORMAT_H2H1H4H3:
            ch->data = modbus_get_float_badc(buf + pos);
            break;
        case CH_FORMAT_H3H4H1H2:
            ch->data = modbus_get_float_cdab(buf + pos);
            break;
        case CH_FORMAT_H4H3H2H1:
            ch->data = modbus_get_float_dcba(buf + pos);
            break;
        default:
            break;
        }
        break;
    }
    case CH_TYPE_INT32:
    {
        int pos = node->reg_add - mb->addr;
        int32_t val = 0;

        switch (node->endian)
        {
        case CH_FORMAT_H1H2H3H4:
            val = modbus_get_int32_abcd(buf + pos);
            break;
        case CH_FORMAT_H2H1H4H3:
            val = modbus_get_int32_badc(buf + pos);
            break;
        case CH_FORMAT_H3H4H1H2:
            val = modbus_get_int32_cdab(buf + pos);
            break;
        case CH_FORMAT_H4H3H2H1:
            val = modbus_get_int32_dcba(buf + pos);
            break;
        default:
            break;
        }

        ch->data = val;
        break;
    }
    case CH_TYPE_UINT32:
    {
        int pos = node->reg_add - mb->addr;
        uint32_t val = 0;

        switch (node->endian)
        {
        case CH_FORMAT_H1H2H3H4:
            val = modbus_get_uint32_abcd(buf + pos);
            break;
        case CH_FORMAT_H2H1H4H3:
            val = modbus_get_uint32_badc(buf + pos);
            break;
        case CH_FORMAT_H3H4H1H2:
            val = modbus_get_uint32_cdab(buf + pos);
            break;
        case CH_FORMAT_H4H3H2H1:
            val = modbus_get_uint32_dcba(buf + pos);
            break;
        default:
            break;
        }

        ch->data = val;
        break;
    }
    case CH_TYPE_UINT16:
    {
        int pos = node->reg_add - mb->addr;
        uint16_t val = 0;

        switch (node->endian)
        {
        case CH_FORMAT_H1H2:
            val = modbus_get_uint16_ab(buf + pos);
            break;
        case CH_FORMAT_H2H1:
            val = modbus_get_uint16_ba(buf + pos);
            break;
        default:
            break;
        }

        ch->data = val;
        break;
    }
    case CH_TYPE_INT16:
    {
        int pos = node->reg_add - mb->addr;
        int16_t val = 0;

        switch (node->endian)
        {
        case CH_FORMAT_H1H2:
            val = modbus_get_int16_ab(buf + pos);
            break;
        case CH_FORMAT_H2H1:
            val = modbus_get_int16_ba(buf + pos);
            break;
        default:
            break;
        }

        ch->data = val;
        break;
    }
    case CH_TYPE_INT8:
    {
        int pos = node->reg_add - mb->addr;
        int v = 0;

        switch (node->endian)
        {
        case CH_FORMAT_HIGH:
            v = (int8_t)((buf[pos] >> 8) & 0xff);
            break;
        case CH_FORMAT_LOW:
            v = (int8_t)(buf[pos] & 0xff);
            break;
        default:
            break;
        }

        ch->data = v;
        break;
    }
    case CH_TYPE_UINT8:
    {
        int pos = node->reg_add - mb->addr;
        int v = 0;

        switch (node->endian)
        {
        case CH_FORMAT_HIGH:
            v = (uint8_t)((buf[pos] >> 8) & 0xff);
            break;
        case CH_FORMAT_LOW:
            v = (uint8_t)(buf[pos] & 0xff);
            break;
        default:
            break;
        }

        ch->data = v;
        break;
    }
    case CH_TYPE_16_1BIT:
    {
        int pos = node->reg_add - mb->addr;

        int v;

        switch (node->endian)
        {
        case CH_FORMAT_16BIT_B0:
            v = (buf[pos] >> 0) & 0x01;
            break;
        case CH_FORMAT_16BIT_B1:
            v = (buf[pos] >> 1) & 0x01;
            break;
        case CH_FORMAT_16BIT_B2:
            v = (buf[pos] >> 2) & 0x01;
            break;
        case CH_FORMAT_16BIT_B3:
            v = (buf[pos] >> 3) & 0x01;
            break;
        case CH_FORMAT_16BIT_B4:
            v = (buf[pos] >> 4) & 0x01;
            break;
        case CH_FORMAT_16BIT_B5:
            v = (buf[pos] >> 5) & 0x01;
            break;
        case CH_FORMAT_16BIT_B6:
            v = (buf[pos] >> 6) & 0x01;
            break;
        case CH_FORMAT_16BIT_B7:
            v = (buf[pos] >> 7) & 0x01;
            break;
        case CH_FORMAT_16BIT_B8:
            v = (buf[pos] >> 8) & 0x01;
            break;
        case CH_FORMAT_16BIT_B9:
            v = (buf[pos] >> 9) & 0x01;
            break;
        case CH_FORMAT_16BIT_B10:
            v = (buf[pos] >> 10) & 0x01;
            break;
        case CH_FORMAT_16BIT_B11:
            v = (buf[pos] >> 11) & 0x01;
            break;
        case CH_FORMAT_16BIT_B12:
            v = (buf[pos] >> 12) & 0x01;
            break;
        case CH_FORMAT_16BIT_B13:
            v = (buf[pos] >> 13) & 0x01;
            break;
        case CH_FORMAT_16BIT_B14:
            v = (buf[pos] >> 14) & 0x01;
            break;
        case CH_FORMAT_16BIT_B15:
            v = (buf[pos] >> 15) & 0x01;
            break;
        default:
            break;
        }
        ch->data = v;

        break;
    }
    default:
        break;
    }
}

static void modbus_memcpy_regs(uint16_t *buf,
                               cfg_pu_t *pu,
                               cfg_frame_t *mb,
                               cfg_su_t *su)
{
    int i;
    if (su->loop <= 1)
    {
        for (i = 0; i < su->valcntsum; i++)
        {
            cfg_ch_t *ch = su->ch + i;
            cfg_node_t *node = ch->node;

            if (node->id < mb->start_id || node->id > mb->end_id)
            {
                continue;
            }

            date_update(buf,pu,mb,ch);
        }
    }
    else
    {
        cfg_subdev_t *subdev = su->subdev_list;
        while (subdev)
        {
            for (i = 0; i < su->valcntsum; i++)
            {
                cfg_ch_t *ch = su->ch + i;
                cfg_node_t *node = ch->node;

                if (node->id < mb->start_id || node->id > mb->end_id)
                {
                    continue;
                }

                date_update(buf,pu,mb,ch);
            }
            subdev = subdev->next;
        }
    }
}

static void collect_offline(modbus_t *ctx, cfg_pu_t *pu, cfg_su_t *su)
{
    int max_retrycnt = 3;

    su_param_t *param = (su_param_t *)su->param;
    if (++param->offline_cnt >= max_retrycnt)
    {
        param->offline_cnt = 0;
        // 离线事件产生
        if (su->state == 1)
        {
            su->state = 0;
            // 发送状态变化COV
            UTIL_LOG_I(pu, "%s _state offline", su->name);
            // msg_cov_send(pu, su->name, "cov", "_state", "0");
        }
    }
}

static void collect_online(modbus_t *ctx, cfg_pu_t *pu, cfg_su_t *su)
{
    if (su->state == 0)
    {
        su->state = 1;
        // 发送状态变化COV
        UTIL_LOG_I(pu, "%s _state online", su->name);
        // msg_cov_send(pu, su->name, "cov", "_state", "1");
    }
}

void collect_frame(modbus_t *ctx, cfg_pu_t *pu)
{
    uint16_t reg_buf[MODBUS_MAX_READ_REGISTERS] = {0};
    uint8_t bit_buf[MODBUS_MAX_READ_BITS] = {0};
    cfg_su_t *su = pu->su;
    while (su)
    {
        su_param_t *param = (su_param_t *)su->param;
        cfg_frame_t *mb = param->frames;

        while (mb)
        {
            pu->pkg_send++;
            pthread_mutex_lock(&pu->hanlder.lock);
            msleep(10);
            modbus_flush(ctx);

            modbus_set_type(ctx, 0); // 0-addr
            modbus_set_slave(ctx, su->addr);

            int err = -1;
            switch (mb->func)
            {
            case 1:
                err = modbus_read_bits(ctx, mb->addr, mb->len, bit_buf);
                break;
            case 2:
                err = modbus_read_input_bits(ctx, mb->addr, mb->len, bit_buf);
                break;
            case 3:
                err = modbus_read_registers(ctx, mb->addr, mb->len, reg_buf);
                break;
            case 4:
                err = modbus_read_input_registers(ctx, mb->addr, mb->len, reg_buf);
                break;
            default:
                break;
            }

            pthread_mutex_unlock(&pu->hanlder.lock);

            if (err < 0)
            {
                collect_offline(ctx, pu, su);
                break;
            }
            else
            {
                // 如果采集成功，需要将断线次数重置
                param->offline_cnt = 0;
                pu->pkg_recv++;
                collect_online(ctx, pu, su);

                if (mb->func == 3 || mb->func == 4)
                {
                    modbus_memcpy_regs(reg_buf, pu, mb, su);
                }
            }

            int frame_interval = 1000;
            if (frame_interval > 0)
            {
                msleep(frame_interval);
            }
            mb = mb->next;
        }
        su = su->next;
    }
}
